<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        .circular{
            width: 15px;
            height: 15px;
            border: 2px solid;
            border-color: palevioletred palegreen palegreen palevioletred;
            border-radius: 20px;
            cursor: pointer;
            transform: rotateZ(137deg);
            transition: 2s all;
        }
        .circular:hover{
            transform: rotateZ(780deg);
            transition: 2s all;
        }
        .active{
            background-color: yellow;
        }
        body{
            border: 1px solid pink;
        }
    </style>
</head>

<body>
    <p style="color: pink">★★★★★☆☆☆☆☆</p>
    <textarea style="resize: none;"></textarea>
    <div class="circular"></div>
    <!-- <button onclick="myFunction()">点击触发函数</button> -->
    <button class="throttle">节流</button>
</body>
<script>
    // let circular = document.querySelector('.circular');
    // let text = document.querySelector('textarea');
    // let flag = false;
    // circular.addEventListener('click',(e)=>{
    //     if(!flag){
    //         circular.classList.add('active');
    //         flag = true;
    //         text.disabled = true;
    //     }else{
    //         circular.classList.remove('active');
    //         flag = false;
    //         text.disabled = false;
    //     }
    // })

    
    // 比较时间
    // const time1 = "2022-03-02 09:00:10";
    // const time2 = "2022-03-02 09:00:01";
    // const overtime = time1 < time2;
    // console.log(overtime);

    // 格式化money
    // 第一种方法
    // const num = 10489467456400;
    // const ThousandNum = num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    // console.log(ThousandNum);
    // 第二种方法
    // console.log((1234567890656).toLocaleString())

    // 生成随机id
    // const ids = 8;//生成多少位数的id
    // const RandomId = Math.random().toString(36).substr(3, ids);
    // console.log(RandomId);

    // 精确小数（具体精准到多少位可以自行设置）
    // const num = 1.8545111;
    // const RoundNum = Math.round(num * 10 ** 1) / 10 ** 1;//3是精准的小数位数（自带四舍五入）
    // console.log(RoundNum);

    // 生成1到10的随机数
    // const RandomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
    // const num = RandomNum(1, 10);
    // console.log(num);

    // const obj = {a: 0, b: 1, c: 2};
    // function func(){
    //     console.log('函数执行了');
    // }
    // Object.keys(obj).length && func(); //判断对象是否为空对象，如果不是空对象再执行函数

    // const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8].slice().sort(() => Math.random() - .5);
    // console.log(arr);//模糊化数据(即：把数组随机排序)

    // let a = 0;
    // let b = 1;
    // [a, b] = [b, a];//结合解构赋值给数据交换值

    // //保留小数后两位(不四舍五入)
    // let num = 8.81888
    // //不四舍五入 向下取整
    // num = Math.floor(num * 100) / 100;
    // console.log(num); //8.81

    // 把对象变成只可读的对象
    // let obj = {a:8,b:9}
    // Object.freeze(obj);
    // obj.a = 20;
    // console.log(obj.a);//8

    // 获取数组中的随机项
    // const arr = ['a', 'b', 'c', 1, 2, 3]
    // const randomItemArray =  arr[Math.floor(Math.random() * arr.length)];
    // console.log(randomItemArray);

    // 获取数组中的最大数
    // 第一种方法
    // const maxNumber = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);
    // console.log(maxNumber([4,9,5,7,2,78,99,188,7,34]));
    // 第二种方法
    // let arr = [4,9,5,7,2,78,99,188,7,34];
    // let newArr = arr.reduce((a,b) => a>b?a:b); // 输出: 9
    // console.log(newArr);

    // 数组累加器简写的方法
    // let arr = [1,2,3,4,5,6];
    // arr.reduce((a,b) => a+b); // 输出: 21

    // 获取数组中的最小数
    // 第一种方法
    // const maxNumber = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);
    // console.log(maxNumber([4,9,5,7,2,78,99,188,7,34,2]));
    // 第二种方法
    // let arr = [4,9,5,7,2,78,99,188,7,34];
    // let newArr = arr.reduce((a,b) => a<b?a:b); // 输出: 9
    // console.log(newArr);

    // 计算数组中元素的出现次数
    // const arr = [1,2,3,1,2,3,4,5,6,'nba','cba','nba','cba']
    // let countedNames = arr.reduce((obj, name) => {
    // if (name in obj) {
    //     obj[name]++
    // } else {
    //     obj[name]=1
    // }
    // return obj
    // }, {})  //reduce的第二个参数就是obj的初始值
    // // 方法一
    // for (const key in countedNames) {
    //     console.log(`${key}成员出现了${countedNames[key]}次`);
    // }
    // // 方法二
    // // Object.keys(countedNames).forEach(el=>{
    // //     console.log(`${el}成员出现了${countedNames[el]}次`);
    // // })

    // reduce参数详解
    // let arr = [1,2,3,4,5,6]
    // let count = arr.reduce((accumulator, currentValue, currentIndex, array)=>{
    //     // console.log(accumulator, currentValue, currentIndex, array);
    //     // console.log(array[currentIndex]);
    //     return accumulator+=currentValue
    // }, 0)
    // console.log(count);
    // // 参数说明
    // // 第一个参数是function
    // // accumulator：上一次调用回调返回的值，或者是提供的初始值（initialValue）
    // // currentValue：数组中正在处理的元素
    // // currentIndex：数据中正在处理的元素索引，如果提供了 initialValue ，从0开始；否则从1开始
    // // array： 调用 reduce 的数组
    // // 第二个参数是初始化值（第一次循环的accumulator值）
    // // 可选项，其值用于第一次调用 callback 的第一个参数。如果没有设置初始值，则将数组中的第一个元素作为初始值

    // in 的用法：判断某个对象内是否拥有某个属性
    // let obj = {a:1,b:2,c:3};
    // let obj2 = {a:1,b:2};
    // for (const key in obj) {
    //     if(key in obj2){
    //         console.log(key+':'+obj[key]);
    //     }
    // }

    // 检查对象中是否存在某个属性
    // const person = {
    // id: 'ak001',
    // name: 'ak47'
    // }
    // console.log('name' in person); // true
    // console.log('isActive' in person); // false
    // console.log(person.hasOwnProperty('id'));//true 检查对象中是否存在某个属性

    // const str = 123;
    // console.log(JSON.stringify(str));//数字转字符串

    // function guid() {  //生成唯一标识的方法
    // return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    // 	var r = Math.random() * 16 | 0,
    // 		v = c == 'x' ? r : (r & 0x3 | 0x8);
    //         return v.toString(16);
    //     });
    // }
    // const onlys = guid();
    // console.log(onlys);

    // function guid2() { //生成唯一标识的方法
    // function S4() {
    // 	return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    //     }
    //     return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    // }
    // const onlys = guid2();
    // console.log(onlys);

    // const obj = { a: 1, b: 2, c: 3 };
    // const newObj = Object.entries(obj);//将对象的key和value转化为一个二维数组
    // console.log(newObj);

    // 替换指定的全部字符串内容
    // let str = '2022/08/08 11:00:00';
    // let newStr = str.replaceAll('/','-');
    // console.log(newStr);

    // 判断一个值是否为NaN
    // console.log(isNaN(NaN));

    // 切割键盘回车的 ‘\n' 字符成一个数组
    // let textarea = document.querySelector('textarea');
    // textarea.addEventListener('input',function(e){
    //         if(e.target.value){
    //             const str = e.target.value;
    //             console.log(str.split('\n'));
    //         }
    // })

    // Promise.all的用法
    // function fn(){
    // return new Promise((resolve,reject)=>{
    //     let randomNum = parseInt(Math.random()*6+1);
    //     console.log(randomNum);
    //     if(randomNum>3){
    //         resolve('买');
    //     }
    //     else{
    //         reject('不买');
    //     }
    // })
    // }
    // async function test(){
    //     try{
    //     let res = await Promise.all([fn(),fn()]);
    //     console.log(res,'success');
    //     }
    //     catch(error){
    //         console.log(error,'error');
    //     }
    // }
    // test();

    // let num = 80;
    // console.log(num/3);//3
    // console.log(num%2);//  取余% 和 除于/ 的区别就是取余如果一个数不能被整除则返回它的余数

    // let str = 123;
    // console.log(str.toString(),String(str));//转换为字符串的方法

    // let date = Date.now();//获取当前时间戳
    // console.log(date);

    //     try {  //在try块中执行的代码里有错误就会跳到catch捕获错误原因
    //    const num = 0;
    //     if(!num) throw '未定义';//throw是抛出错误原因然后在catch中捕获该错误原因
    //     } catch(err) {
    //     // alert(err.name); // 引用错误
    //     alert(err); // Lalala变量没有定义
    //     // alert(err.stack); //引用错误：Lalala变量没有定义在…
    //     // 也可以作为整体显示错误
    //     // “name: message”：可以将错误转换为字符串
    //     // alert(err); // ReferenceError: lalala is not defined
    //     } finally{  //如果它存在，它会在所有情况下运行：1、在try之后执行，如果没有错误，2、在catch之后执行，如果有错误
    //         console.log('我是不管上面的结果，都会执行的代码');
    //     }


    // async、await结合try一起使用
    // 1、async、await是异步执行，async 里面后面的代码会等 await 异步完再执行。
    // 2、try 里有错误，执行 catch
    // async function getSubCategoryData () {
    //   try { // 放置的是可能出现异常的代码块
    //     // 异步获取数据
    //     const res = await getSubCategories()
    //     // 将获取到的子分类数据放置到 data 中
    //     console.log('子数据：', res)
    //     this.subs = res.categories
    //   } catch (err) { // 放置的是出现异常后处理异常的代码块
    //     console.log('异常：', err)
    //   } finally {
    //     // 不管是否出现异常，都会执行的代码块
    //   }
    // }


    // 交换数组元素的位置
    //     const swapWay = (arr, i, j) => {
    //     let newArr = [...arr];
    //     [newArr[j],newArr[i]] = [newArr[i],newArr[j]];
    //     return newArr;
    //     };
    //    let callback =  swapWay([1,2,3],2,0);//参数说明：第一个要传一个数组，第二个是要更换改数组里的哪个下标的元素，第三个是把要更换到哪个位置
    //    console.log(callback);


    // let ps1 = new Promise((resolve, reject) => {
    // setTimeout(() => {
    //     resolve('success-1')
    // }, 600)
    // })

    // let ps2 = new Promise((resolve, reject) => {
    //     setTimeout(() => {
    //         resolve('success-2')
    //     }, 5000)
    // })

    // let pf1 = new Promise((resolve, reject) => {
    //     setTimeout(() => {
    //         reject('fail-1')
    //     }, 400)
    // })

    // let pf2 = new Promise((resolve, reject) => {
    //     setTimeout(() => {
    //         reject('fail-2')
    //     }, 200)
    // })
    // // Promise.allSettled 中的 Promise 数组全部执行完才会返回一个对象数组，
    // // 其中包含了每个 Promise 的执行结果，且都有一个 status 字段，fulfilled 表示成功，rejected 表示失败。
    // // Promise.allSettled 返回结果数组 arr 与请求的 Promise 数组顺序一致，与异步请求响应返回的顺序无关。
    // // Promise.allSettled 可通过返回的数组中的 status 字段，用于判断成功和失败的数量，如上传图片成功几张、失败几张。
    // Promise.allSettled([ps1, ps2, pf1, pf2]).then(arr => {   //注意它的回调then是看最慢执行的那个执行完成才会执行该then回调
    //     console.log(arr) // 输出：[{status: "fulfilled", value: "success-1"}, {status: "fulfilled", value: "success-2"}, {status: "rejected", reason: "fail-1"}, {status: "rejected", reason: "fail-2"}]
    // })

    // // Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。所有的 Promise 对象都成功时返回的是一个结果数组，
    // // 一旦有任何一个 Promise 对象失败则立即返回失败。
    // // Promise.all 可用于类似一个页面有多个请求，需要 loading 等待多个请求都执行完再渲染数据，有一个请求异常时，跳转错误页面。
    // // 注意：Promise.all 的正确返回结果数组 arr 与请求的 Promise 数组顺序一致，与异步请求响应返回的顺序无关。
    // Promise.all([ps1, ps2, pf1, pf2]).then(arr => {   //注意它的回调then是看最慢执行的那个执行完成才会执行该then回调
    //     console.log(arr)
    // })

    // // 顾名思义，Promise.race 是赛跑的意思，Promise.race 中的 Promise 数组中谁先返回成功或失败的结果，整体就返回那个结果。
    // // Promise.race 中的各 Promise 的执行顺序是无序的。可用于对某些不支持设置 timeout 的请求模拟请求超时和中止请求。
    // Promise.race([ps1, ps2, pf1, pf2]).then(arr => {   //注意它的回调then是看最慢执行的那个执行完成才会执行该then回调
    //     console.log(arr)
    // })

    // let str = 'abcdefg';
    // console.log(str.slice(2,5));//第一个参数是从哪个下标开始截取，第二个参数是在哪个下标结束截取


    // let str = 'aaabbbcccdddeee';
    // arr = [...new Set(str)].join('')//字符串去重的方法1

    // let arr = '';
    // for (const k in str) { 
    // //    if(!arr.includes(str[k])){//字符串去重的方法2
    // //     arr+=str[k]
    // //     }
    //    if(arr.indexOf(str[k])===-1){//字符串去重的方法3
    //     arr+=str[k]
    //     }
    // }
    // console.log(arr);

    // const arr = [1,2,3]
    // const obj = {...arr}//数组转为对象:数组的下标将会成为对象的key
    // console.log(obj)

    // let obj1 = {a:1};
    // let obj2 = Object.create(null,{a:{value:1,writable:true,configurable:true}})
    // // 而使用Object.create(null)创建的对象，除了自身属性a之外，原型链上没有任何属性。
    // // 也就是我们可以通过Object.create(null)这种方式创建一个纯净的对象，
    // // 我们可以自己定义hasOwnProperty、toString等方法，完全不必担心会将原型链上的同名方法覆盖掉。
    // console.log(obj1,obj2);

    // let a =8,b = 10;
    // [a,b] = [b,a] //通过解构交换两个变量的值 注意只能交换let和var定义的变量，const定义的会报错
    // console.log(a,b);

    // 1.对象和布尔值进行比较
    // [] == false; //true
    // []对象——>字符串——>数值0
    // false——>数值0
    // 2.对象和数字进行比较
    // [111] == 111; //true
    // [111]对象——>字符串——>数值111
    // 3.对象和字符串进行比较
    // [1,2,3]== '1,2,3'; //true
    // [1,2,3,]对象——>字符串'1,2,3,'

    // let ab = (function(a){  //自执行函数的使用
    //     return a;
    // })(888)
    // console.log(ab);

    // let arr = ['星期天','星期一','星期二','星期三','星期四','星期五','星期六'];
    // let day = new Date().getDay();
    // console.log(arr[day]); //获取今天是星期几
            

    //首先函数会优先提升
    //变量的定义后提升 后提升并不会把先提升的覆盖掉
    //变量提升只会先提升定义 不会提升值
    // fun();//弹出B
    // var fun = function() {
    //     alert('A');
    // }

    // function fun() {
    //     alert('B')
    // }
    // fun();//弹出A


    // let flag;
    // let flagTwo;
    // let flagthree;
    // flag = flagTwo = flagthree = true;//一次性给多个变量赋值
    // console.log(flagTwo,flag,flagthree);

    // let a = 20;
    // function fn(){
    //     a++;  //等于：函数内部的var变量提升为undefined
    //     console.log(a);
    //     var a = 88; //同一变量名不可以var与let一起声明
    //     console.log(a);
    // }
    // console.log(a);
    // fn();
    // console.log(a);

    // function addCount() {
    //         var count = 0;
    //         return function() {
    //             console.log(count);
    //             count = count + 1;
    //             // console.log(count);
    //         }
    //     }
    // var fun1 = addCount();
    // var fun2 = addCount();
    // fun1(); //1  //不同函数名调用不会影响函数的累计变化，因为他们的内存指针不是指向同一个内存地址,同一个函数名调用才会影响
    // fun2(); //1
    // fun2(); //2
    // fun1(); //2
    
    // 在循环中缓存数组长度
    // let array = [1,2,3,4,5]
    // for (let i = 0, length = array.length; i < length; i++) {
    //     console.log(array[i]);
    // }

    // function fn(num){
    //     if(num<=1){
    //         return 1
    //     }
    //     return num + arguments.callee(num-1); 
    //     //递归函数的高雅写法  arguments.callee代表函数名，多用于递归调用，防止函数执行与函数名紧紧耦合的现象，对于没有函数名的匿名函数也非常起作用
    // } 
    // let result = fn(5);
    // console.log(result);


    // fn();//alert('fn函数222')
    // var fn = function (){ //匿名函数不会函数提升
    //     alert('fn函数111')
    // }

    // function fn(){  //正常定义的函数会函数提升
    //     alert('fn函数222')
    // }

    // fn();//alert('fn函数111')

    // let obj = {a:10,b:{c:8}};
    // console.log(obj.b ?. d ?? 'default');//?.运算符可以判断一个对象内是否真实拥有指定的属性，如果没有则返回undefined，结合??使用，如果没有指定的属性则使用双问号后的值
    
    // var deepCopy = (obj) => { //简易版深拷贝
    // var ret = {}
    // for (var key in obj) {
    //     var value = obj[key]
    //     if(value instanceof Array){
    //         ret[key] = Object.values(deepCopy(value));
    //     }else{
    //         ret[key] = typeof value === 'object' ? deepCopy(value) : value
    //     }
    // }
    // return ret
    // }
    // let obj = {a:1,b:{c:2,d:[1,2,3],e:{f:function(){}}}}
    // // let obj2 = deepCopy(obj);
    // // console.log(obj,obj2);


    // //使用递归的方式实现数组、对象的深拷贝
    // function deepClone1(obj) {
    // //判断拷贝的要进行深拷贝的是数组还是对象，是数组的话进行数组拷贝，对象的话进行对象拷贝
    // var objClone = Array.isArray(obj) ? [] : {};
    // //进行深拷贝的不能为空，并且是对象或者是
    // if (obj && typeof obj === "object") {
    //     for (key in obj) {
    //     if (obj.hasOwnProperty(key)) {
    //         if (obj[key] && typeof obj[key] === "object") {
    //         objClone[key] = deepClone1(obj[key]);
    //         } else {
    //         objClone[key] = obj[key];
    //         }
    //     }
    //     }
    // }
    // return objClone;
    // }
    // let obj2 = deepClone1(obj);
    // console.log(obj,obj2);

    // let arr = [1, [2, [3, 4,[5,6,{a:7,b:[8,[9,[10,[11,[12]]]]]}]]]];//数组扁平化---对象里的数组也扁平化
    // function flatten(arr){
    //     return arr.reduce((prev, next)=>{
    //         if(Object.prototype.toString.call(next)=='[object Object]'){
    //             for (const k in next) {
    //                 if(Array.isArray(next[k])){
    //                     next[k] = next[k].flat(Infinity)
    //                 }
    //             }
    //         }
    //         return prev.concat(Array.isArray(next)?flatten(next):next)
    //     },[])
    // }
    // console.log(flatten(arr));

    // 反转字符串
    // let str = 'hello world';
    // const machin = str=> str.split('').reverse().join('');
    // console.log(machin(str));


    // (function (){
    // console.log("666");
    // // }())  //自执行函数的自动调用方法1
    // })()     //自执行函数的自动调用方法2

    // let obj = {
    //     a:1
    // }
    // function fn(arg){
    //     console.log(arg,this);
    // }
    // fn.apply(obj,[111])//注意：通过apply改变this，如果传递的参数不是数组会报错；

    // const [a, b, c, d, e] = "hello";//数组的解构赋值
    // console.log(a, b, c, d, e);//h e l l o

    // const { s,b:z } = {s:123,b: true};//该解构赋值意为把变量b的名字改为z，赋值还是会正常赋值
    // console.log(s,z);

    // let arr = [1,2,3];
    // let newArr = arr.find(el=>el>2); 
    //find与some的区别在于find找出符合条件的第一个元素然后把该元素返回，如果找不到则直接返回undefined
    //some是找出符合条件的第一个元素然后返回布尔值true
    
    // 偶数 & 1 = 0
    // 奇数 & 1 = 1
    // console.log(7 & 1);    // 1
    // console.log(8 & 1) ;   // 0

    // Object.is() 判断两个值是否相同。如果下列任何一项成立，则两个值相同：
    // 两个值都是 undefined
    // 两个值都是 null
    // 两个值都是 true 或者都是 false
    // 两个值是由相同个数的字符按照相同的顺序组成的字符串
    // 两个值指向同一个对象
    // 两个值都是数字并且
    // 都是正零 +0
    // 都是负零 -0
    // 都是 NaN
    // 都是除零和 NaN 外的其它同一个数字

    // 判断两个对象相等的方法 (其顺序和数据类型也要相同才能相等)
    // let obj1 = {a:1,b:2};
    // let obj2 = {a:1,b:2};
    // console.log(JSON.stringify(obj1) === JSON.stringify(obj2));//true

    // 判断两个数组相等的方法 (其顺序和数据类型也要相同才能相等)
    // let arr = [1,2,3];
    // let arr2 = [1,2,3];
    // console.log(JSON.stringify(arr) === JSON.stringify(arr2));//true

    // let start = performance.now();//测试代码的执行耗时
    // let sum = 0;
    // for (let i = 0; i < 100000; i++) { 
    // sum += 1;
    // }
    // let end = performance.now();//测试代码的执行耗时
    // console.log(start);
    // console.log(end);

    // let a = eval('["image1.png", "image2.png", "image3.png"]');//eval可以将字符串转为一个非字符串的值，eval函数是强大的数码转换引擎,字符串经eval转换后得到一个javascript对象
    // console.log(a);

    // 为类数组(arguments和nodeList)添加数组方法push,pop
    // (function(){
    // Array.prototype.push.call(arguments,'王五');//为伪数组添加push方法，原理：在数组原型上的push函数改变this指向，使其指向arguments伪数组
    // console.log(Array.from(arguments));//伪数组转真数组方法1
    // console.log(Array.of(...arguments));//伪数组转真数组方法2
    // console.log(new Array(...arguments));//伪数组转真数组方法3
    // })('张三','李四')

    // Function.prototype.newCall = function(context, ...parameter) {
    //     console.log(parameter,...parameter);
    // if (typeof context === 'object' || typeof context === 'function') {
    //     context = context || window
    // } else {
    //     context = Object.create(null)
    // }
    // let fn = Symbol()
    // context[fn] = this
    // const res =context[fn](...parameter)
    // delete context.fn;
    // return res
    // }
    // let person = {
    // name: 'Abiel'
    // }
    // function sayHi(age,sex) {
    //     console.log(this);
    // console.log(this.name, age, sex);
    // }
    // sayHi.newCall(person, 25, '男'); // Abiel 25 男

    // 使用工厂模式创建对象
    // function createPerson(name){
    // var o = Object.create(null);//创建一个纯净的对象
    // o.name = name;
    // return o; 
    // }
    // var person1 = createPerson('张三');
    // console.log(person1);

    // let obj = Object.create(null,{a:{value:1,writable:true,configurable:true}});
    // console.log(Object.getOwnPropertyDescriptors(obj));//获取对象身上隐藏的属性

    // 多层数组遍历：使用递归实现
    // let treeNodes = [
    // {
    //  id: 1,
    //  children: [
    //    {
    //     id: 2,
    //     children: [
    //      {
    //       id: 3,
    //       children:[]
    //       },
    //       {
    //         id: 4,
    //        }
    //       ]
    //      },
    //      {
    //       id: 5,
    //       children: []
    //      }
    //      ],
    //      users: []
    //     },
    //   ];
    //   let parseTreeJson = function(treeNodes){
    //   if (!treeNodes || !treeNodes.length) return;
    //    for (var i = 0, len = treeNodes.length; i < len; i++) {
    //         var childs = treeNodes[i].children;
    //         console.log(treeNodes[i].id);
    //         if(childs && childs.length > 0){
    //             parseTreeJson(childs);
    //         }
    //    }
    // };
    // parseTreeJson(treeNodes);


    // function deepClone(source){ //数组或者对象的深拷贝
    // const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
    // for(let keys in source){ // 遍历目标
    //     if(source.hasOwnProperty(keys)){
    //     if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象，就递归一下
    //         targetObj[keys] = source[keys].constructor === Array ? [] : {};
    //         targetObj[keys] = deepClone(source[keys]);
    //     }else{ // 如果不是，就直接赋值
    //         targetObj[keys] = source[keys];
    //     }
    //     }
    // }
    // return targetObj;
    // }
    // let obj2 = deepClone(treeNodes);

    // 数组扁平化---简称多维数组转一维数组
    // function flatten(arr) {
    //     while(arr.some(item=>Array.isArray(item))) { //while循环的条件，如果不是数组则不执行循环
    //         arr = [].concat(...arr);
    //     }
    //     return arr;
    // }
    // console.log(flatten([1,[2,3,[4,5]]])); //[1,2,3,4,5]

    // let fs = new Fn('test');//构造函数也会函数提示，在定义之前也可以访问
    // console.log(fs.son());
    // function Fn(p1){
    //    this.p1 = p1;
    //    this.son = ()=>{
    //     return this.p1+' - Hai';
    //    }
    // }

    // (function() { //自执行函数（又名：立即执行函数）
    //     console.log(this) //匿名函数调用，this指向window
    // })()

    // let throttle = document.querySelector('.throttle');
    // let timer = null;
    // throttle.addEventListener('click',()=>{ //节流函数
    //     if(!timer){
    //         timer = setTimeout(()=>{
    //             console.log(111);
    //             timer = null;
    //         },1000)
    //     }
    // })

    // throttle.addEventListener('click',()=>{ //防抖函数
    //     if(timer){
    //         clearTimeout(timer);
    //         timer = null;
    //     }
    //     timer = setTimeout(()=>{
    //         console.log(111);
    //     },500)
    // })

    // window.onbeforeunload = function (e) { //浏览器刷新事件
    //     e = e || window.event;
    //     if (e) {
    //         e.returnValue = '关闭提示';
    //     }
    //     return '关闭提示';
    // };
    
    // let f1 = new Promise((resolve,reject)=>{
    //     setTimeout(()=>{
    //         resolve(1)
    //     },300)
    // })
    // let f2 = new Promise((resolve,reject)=>{
    //     setTimeout(()=>{
    //         reject(2)
    //     },500)
    // })
    // Promise.all([f1,f2]).then((res)=>{ //Promise.all如果全部的promise都返回resolve才会执行以下then回调
    //     console.log(res);
    // }).catch((err)=>{ //Promise.all如果有一个promise返回了reject，就会执行以下catch回调;如果有多个reject，则只会返回请求速度最快的reject，其他不会被执行
    //     console.log(err);
    // })

    //    const fn = ()=>{
    //     console.log(arguments);//此处会报错，注意：箭头函数没有arguments
    //    };
    //    fn(1,2,3)

    // intanceof 操作符的实现原理及实现
    // instanceof 运算符用于判断构造函数的 prototype 属性是否出现
    // 在对象的原型链中的任何位置
    // function myInstanceof(left,rigth){
    //     let p = Object.getPrototypeOf(left);
    //     let prototype = rigth.prototype;
    //     console.log(p,prototype);
    //     while(true){
    //         if(p===prototype) return true;
    //         if(p) return false;
    //         p = Object.getPrototypeOf(p);
    //     }
    // }
    // console.log(myInstanceof([],Array));

    // js同步执行代码问题
    // function takeLongTime() { 
    //     return new Promise(resolve => {
    //         setTimeout(()=>{
    //             console.log(222);
    //             resolve()
    //         },1000)
    //     })
    // }
    // async function test() { 
    //    await takeLongTime();
    //     console.log(111)
    // }
    // test();

    // async function test1() {
    //     console.log('1');
    //     console.log(await test2());//等待一个promise的状态
    //     console.log('2');
    // }
    // async function test2() {
    //     console.log('3');
    //     return await 'return test2 value'
    // }
    // test1();

    // // 定义一个动物类
    // function Animal (name) {
    // // 属性
    // this.name = name || 'Animal';
    // // 实例方法
    // this.sleep = function(){ //如果实例的方法与其原型上的方法重名，则实例上的会覆盖原型上的
    //     console.log(this.name + '正在睡觉！');
    // }
    // }
    // // 原型方法
    // Animal.prototype.eat = function(food) {
    //     console.log(this.name + '正在吃：' + food);
    // };

    // let cat = new Animal('猫猫');
    // cat.sleep();

    // // 子构造函数
    // function Kitten (){
    //     Animal.call(this,'狗狗');
    // }
    // Kitten.prototype.say = function (){
    //     console.log('can you sing to me');
    // }
    // Kitten.prototype = new Animal();
    // // Kitten.prototype.constructor = Kitten;
    // console.log(Kitten.prototype.constructor);
    // let son = new Kitten();
    // son.sleep();
    // son.eat('shi');
    // cat.eat('翔');

    // let obj = {a: 1};
    // console.log(Object.values(obj));//把对象的值转换为数组
    
    // 运用递归的注意点：递归调用函数时通过console.log()打印有值，但是在获取的时候是undefined，
    // 原因是在递归函数内部没有对递归函数进行return，否则外层函数无法接收到返回值。
    // 如果想要函数最后一次计算所得值，就需要在每次调用该函数的时候进行return，
    // 每一次return都是把最新的函数调用返回给外层的函数调用。
    // let sum = 1;
    // function fn(n){
    //     sum*=n;
    //     if(n==1){
    //         return;//此处一定要return，可以不带返回值直接return
    //     }
    //     fn(n-1);
    //     return sum;//注意：此处的返回值一定要在递归调用函数之后返回；
    // }
    // console.log(fn(3));

    // let arr = [1,2,3];
    // fill方法：当其接受三个参数时，表示对数据的某些数据项进行替换。其中第一个参数表示替换的元素，
    // 第二个参数是起始位置（其参数表示数组中的下标），第三个参数是结束位置（注意：其参数表示结束位置，但不包括此位置）
    // console.log(arr.fill(8,0,2));

    // arr.flat//数组扁平化

    // flatMap方法
    // flatMap能用于在遍历数组期间增加、删除和修改数组元素（也就是修改 items 的数量），当我们需要增加元素时，
    // 就在回调函数中返回一个包含多个元素的数组，如果想要删除某一个元素时，就在回调函数中返回一个空数组（注意是空数组，
    // 返回空字符串、null或者undefined都不会改变数组items 的数量）,如果想要修改元素，则在回调函数中返回一个修改过后的元素即可。

    // 增加元素
	// const msgArr = ["it's Sunny in", "California"];
    // const newArr = msgArr.flatMap(i => i.split(' '))
    // console.log(newArr); //   ["it's", "Sunny", "in", "California"]

    // 删除元素	
	// const newArr = arr.flatMap(x => x % 2 === 0 ? [x]: [])
	// console.log(newArr); // [2, 4]

    // 修改元素
    // const newArr = arr.flatMap(x => [x * 2])
	// console.log(newArr); // [2, 4, 6, 8]

    // let arr = [1,2,3];
    // console.log(arr.at(-1));//获取数组的最后一位元素

    // 获取1-5的随机加减数
    // const problems = [];
    // for (let i = 0; i < 5; i++) {
    // const num1 = Math.floor(Math.random() * 5) + 1;
    // const num2 = Math.floor(Math.random() * 5) + 1;
    // const operator = Math.random() < 0.5 ? '+' : '-';
    // const result = operator === '+' ? num1 + num2 : num1 - num2;
    // if (result > 0 && result < 5) {
    //     problems.push(`${operator}${num2}`);
    // } else {
    //     i--;
    // }
    // }
    // let a = Number(problems[0]);
    // a = 1+a;
    // console.log(a);

    // 区分鼠标单击或者双击事件
    // let body = document.querySelector("body");
    // let flag = false;
    // body.onclick = function(){
    //     flag = false;
    //     setTimeout(function(){
    //         if(flag == true){
    //             return;
    //         }
    //         console.log("鼠标单击11");
    //     },500);
        
    // }
 
    // body.ondblclick = () =>{
    //     flag = true;
    //     console.log("鼠标双击22");
    // }

    // let arr = [1,2,3,4,5,6,7];
    // const removeData = (rowIndex)=>{
    //    arr = arr.filter((_,idx)=>!rowIndex.includes(idx))
    // }
    // removeData([1,4]); 
    // console.log(arr); // 输出结果 [1,3,4,5,7]
    // 期望输出结果 [1,3,4,6,7]

    // 数组同时过滤多个条件
    // let arr = [1,2,3,4,5,6,7];
    // const removeData = (rowIndex,it)=>{
    //     arr = arr.filter(el => (el !== it) && !rowIndex.includes(el))
    // }
    // removeData([1,4],2); 
    // console.log(arr);

    // js字符串中间添加字符
    // function insertCharacter(str, char, index) {
    //     if(str.length>=2){
    //         return str.slice(0, index) + char + str.slice(index);
    //     }else{
    //         return str;
    //     }
    
    // }
    
    // // 用法示例：
    // var result = insertCharacter("95", ".", 1);
    // console.log(result);

    // let arr = [];
    // let a = arr.every(el=>el>1) //空数组会返回true


    // let obj = {a:{b:{c:1}}}
    // const {a:{b:{c}}} = obj;//连续结构复制直接取出c的值
    // 解构赋值重命名可以用const {a:{b:{c:data}}} = obj; //把c重新命名为data
    // console.log(c);

    // let request =  async ()=> { //在请求接口的外部函数使用async
    //     try {
    //         const res = await fetch('https://api.github.com/search/users?q=NBA'); //第一个await代表等待fetch的promise的状态
    //         const data = await res.json(); //第二个await代表等待res返回的promise状态
    //         console.log(data); //直接获取数据
    //     } catch (error) {
    //         // 接口出错或者服务器出错都会走到这里
    //         console.log(error); //报错的原因
    //     }
    // }

    // js实现同步img.onload
    // function checkImagePath(path) {
    //     return new Promise((resolve)=>{
    //         var img = new Image();
    //         img.onload = function() {
    //             resolve(true)
    //         };
    //         img.onerror = function() {
    //             resolve(false)
    //         };
    //         img.src = path;
    //     })
    // }
    // let f;
    // checkImagePath('https://img0.baidu.com/it/u=256816879').then(res=>{
    //     f = res;
    // });
    // setTimeout(() => {
    //     if(f) return;
    //     console.log(111);
    // }, 200);
    
    // let throttle = document.querySelector('.throttle')
    // window.addEventListener('keydown', function(e) {
    //     if(e.keyCode===32){
    //         throttle.style.background = 'skyblue'
    //     }
    // });
    // window.addEventListener('keyup', function(e) {
    //     if(e.keyCode===32){
    //         throttle.style.background = 'none'
    //     }
    // });

    // 函数体只有一句，想返回一个对象，用小括号包着即可
    // let fn = (params=>({params}))
    // console.log(fn(1));

    // Promise的then函数方法如果报错会执行catch方法
    // let arr = [1,2,3,4,5,6,7];
    // let obj = {
    //     list: undefined
    // }
    // function fn(){
    //     return new Promise((resolve)=>{
    //         resolve(111)
    //     })
    // }
    // fn().then(()=>{
    //     obj.list.find(el=>arr.includes(el))
    // }).catch(()=>{
    //     console.log(2);
    // })
    
    // 加工且过滤数组
    // let arr = [1,2,3,4,5];
    // arr = arr.map(el=>{
    //     let obj = {}
    //     if(el>3){
    //         obj= {el}
    //     }
    //     return Object.values(obj).length && obj;
    // }).filter(el=>el)
    // console.log(arr);

    // new Map存储对象的key可以是任意类型
    /* let map = new Map();
    const objKey = { id: 1 };
    map.set(objKey,'张三');
    console.log(map.get(objKey)); */
    
    
    // 以某个目标截取前后的字符串
    // let str = "123.456";
    // let index = str.indexOf(".");
    // if (index !== -1) {
    //     // 截取从字符串开头到小数点之前的部分（不包括小数点）
    //     // let result = str.substring(0, index);

    //     // 截取从字符串小数点后面的部分（不包括小数点）
    //     let result = str.substring(index+1,str.length);
    //     console.log(result);
    // }

    // break跳出整个循环---continue跳出当前循环
    // for (let i = 1; i < 8; i++) {
    //     console.log(i);
    //     if (i === 6) {
    //         break;
    //     }
    // }

    // 利用async await把函数变为异步函数，等待异步请求完成后再执行下一步
    // (async function (){   
    //     let a = 0;
    //      function fn(){
    //         return new Promise((resolve)=>{
    //             setTimeout(() => {
    //                 a = 1;
    //                 resolve()
    //             }, 1000);
    //         })
    //     }
    //    let flag = await fn().then(()=>{
    //         if(a>0){
    //             return true;
    //         }
    //     })
    //     if(flag){
    //        return console.log(1);
    //     }
    //     console.log(2);
    // })()

    // 结构赋值重命名
    // let obj = {a:1,b:2};
    // let {a:g} = obj;
    

    // 接口请求文件流，把文件流转为blob对象，再把blob对象转为url，最后把url渲染到页面上
    //     fetch('http://127.0.0.1/user?name=自动挡&age=18',{
    //         method: 'GET',
    //         headers: {
    //             'Content-Type': 'text/plain'
    //         },
    //     }).then(response => response.blob())
    //   .then(blob => {
    //     const url = URL.createObjectURL(blob);    
    //     const img = document.createElement('img');
    //     img.src = url;
    //     document.body.appendChild(img);
    //   })
    //   .catch(error => console.error('Error:', error));


    // fetch请求接口
    //     fetch('http://127.0.0.1/user',{
    //         method: 'post',
    //         headers: {
    //             'Content-Type': 'application/json'
    //         },
    //         body: JSON.stringify({name:'张三',age:18})
    //     }).then(response => {        
    //        return response.json().then(res=>{
    //             return res?.data?.list
    //        })
    //     })
    //   .then(res => {
    //     console.log(res);
    //   })
    //   .catch(error => console.error('Error:', error));


    // ??运算符的使用---功能：判断一个对象内是否真实拥有指定的属性，如果没有则返回undefined，结合??使用，如果没有指定的属性则使用双问号后的值
    // 与||的区别：||只能判断对象是否真实拥有指定的属性，如果没有则返回false，但是不能判断空字符和0，??可以判断空字符和0
    // let obj = undefined;
    // let data = obj?.a??{a:1};
    // console.log(data);
    

    // 数组的reduce给数组先查重把某个值相加再去重数组
    // const uniqueWithSum = (arr) => {
    //     return arr.reduce((acc, current) => {
    //         // 查找是否已经存在相同名字的元素
    //         // 指向问题---数组find返回的元素指向的是数组中对应的元素
    //         const existing = acc.find(item => item.name === current.name);
    //         if (existing) {
    //             // 如果存在，则累加num属性
    //             existing.num += current.num;
    //         } else {
    //             // 如果不存在，则添加到结果数组中
    //             acc.push(current);
    //         }
    //         return acc;
    //     }, []);
    // };
    // let arr = [
    //     { name: '张三', num: 1 },
    //     { name: '李四', num: 2 },
    //     { name: '张三', num: 3 },
    //     { name: '王五', num: 4 },
    //     { name: '李四', num: 5 },
    // ];
    // console.log(uniqueWithSum(arr));
    
    // 判断数组中是否有重复元素，如果有把元素的num相加再去重数组
    // let arr = [
    //     { name: '张三', num: 1 },
    //     { name: '李四', num: 2 },
    //     { name: '张三', num: 3 },
    //     { name: '王五', num: 4 },
    //     { name: '李四', num: 5 },
    // ];
    // arr.forEach((el,index)=>{
    //    arr.forEach((el2,index2)=>{
    //        if(el.name === el2.name && index !== index2){
    //            el.num += el2.num;
    //            arr.splice(index2,1)
    //        }
    //    })
    // })
</script>

</html>